//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include <iostream>
#include <fstream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <curl/curl.h>
//#include <curl/types.h>
#include <curl/easy.h>

#include "libraries.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGHTINLINE  10000
#define URLWALTZ      "http://waltz.vub.ac.be/results.cgi"
#define NUMBERSEQ     1
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
struct fasta {

  char  *id;
  char  *sequence;

  fasta() {
    this->id       = NULL;
    this->sequence = NULL;
  }
  
  ~fasta() {
    if (this->id)       delete[] this->id;
    if (this->sequence) delete[] this->sequence;
  }

};

#define fasta struct fasta
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace SpaceFASTA {

  bool EqualID(TNode <fasta>* NodeT, void* ref) {
    fasta*  fastaA  = NodeT->Info();
    string* fastaB  = (string*)ref;
    return (strstr(fastaA->id, fastaB->c_str()))?true:false;
  }

};
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
static char   errorBuffer[CURL_ERROR_SIZE];
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void trim(char *text) {

  int i, j;
  for (i = 0, j = 0; i < strlen(text); i++)
    if ((text[i] != '\n') && (text[i] != '\r'))
      text[j++] = text[i];
  text[j] = '\0';

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void Ctrim(char *text) {

  int i, j;
  for (i = 0, j = 0; i < strlen(text); i++)
    if ((text[i] != '\n') && (text[i] != '\r') && (text[i] != ' '))
      text[j++] = text[i];
  text[j] = '\0';

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
static int writer(char *data, size_t size, size_t nmemb, string *buffer) {
  int result = 0;
  if (buffer != NULL) {
    buffer->append(data, size * nmemb);
    result = size * nmemb;
  }
  return result;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
string EraseHTMLTAG(string text) {

  size_t  foundB, foundE;

  do {
    foundB = text.find("<");             if (foundB == string::npos) break;
    foundE = text.find(">", foundB + 1); if (foundE == string::npos) break;
    text.erase(int(foundB), int(foundE));
  } while (true);

  return text;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* SplitColumn(char* buffer, int position, const char* characters) {

  if (!buffer || !characters || (position <= 0)) return NULL;

  char *part = strtok(buffer, characters);
  if (position == 1) return part;

  int tmpPosition = 1;
  do {
    part = strtok(NULL, characters);
    tmpPosition++;
  } while (part && (tmpPosition != position));

  return part;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
string SplitFilename (const string& str) {
  size_t found;
  found = str.find_last_of("/\\");
  return str.substr(found + 1);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int main (int argc, char** argv) {

  String* namefileIn  = NULL;
  String* namefileOut = NULL;

  if (argc < 3) {
    cout << "Error in the input parameters! Must by like: " << endl;
    cout << "./WaltzCompute <input file> <output file>" << endl;
    return 0;
  }
  else {
    cout << "---------------------------------------------------------------------------------" << endl;
    cout << "  Input File : " << argv[1] << endl;
    cout << " Output File : " << argv[2] << endl;
    cout << "---------------------------------------------------------------------------------" << endl;
    namefileIn  = new String(argv[1]);
    namefileOut = new String(argv[2]);
  }

  fstream  fileIn, fileOut;
  char     buffer[LENGHTINLINE + 1], bufferA[LENGHTINLINE + 1], bufferB[LENGHTINLINE + 1];

  fileIn.open(namefileIn->Out(),   fstream::in);
  fileOut.open(namefileOut->Out(), fstream::out);

  if (!fileIn.is_open() || !fileOut.is_open()) {
    if (namefileIn)  delete namefileIn;
    if (namefileOut) delete namefileOut;
    return 0;
  }
  
  int              cumulativeCounter = 0;
  string           query             = "";
  TListE <fasta>  *listData          = new TListE <fasta> ();
  bool             bucleCond         = true;
  
  while (bucleCond) {
    
    if (cumulativeCounter++ < NUMBERSEQ && !fileIn.eof()) {
      
      fasta *fastaData = new fasta ();
        
      fileIn.getline(bufferA, LENGHTINLINE);
      fileIn.getline(bufferB, (LENGHTINLINE - strlen(bufferA) - 10));
      
      if (strstr(bufferB, "X")) { cumulativeCounter--; continue; }

      fastaData->id = new char[strlen(bufferA) + 1];
      query += bufferA;
      Ctrim(bufferA);
      strcpy(fastaData->id, bufferA);
      
      fastaData->sequence = new char[strlen(bufferB) + 1];
      strcpy(fastaData->sequence, bufferB);
      query += "\n";
      query += bufferB;
      query += "\n";
      
      listData->Add(fastaData);
      
      continue;
      
    }     
    
    if (fileIn.eof()) bucleCond = false;
      
    cout << query.c_str() << endl;
  
    struct        curl_httppost *formpost = NULL;
    struct        curl_httppost *lastptr  = NULL;
   
    CURL*         curl;
    CURLcode      result;

    char          url[LENGHTINLINE];

    string        bufferURL;
  
    curl_global_init(CURL_GLOBAL_ALL);

    curl_formadd(&formpost,
                 &lastptr,
                 CURLFORM_COPYNAME, "sequence",
                 CURLFORM_COPYCONTENTS, query.c_str(),
                 CURLFORM_END);
  
    curl_formadd(&formpost,
                 &lastptr,
                 CURLFORM_COPYNAME, "threshold",
                 CURLFORM_COPYCONTENTS, "92",
                 CURLFORM_END);

    curl_formadd(&formpost,
                 &lastptr,
                 CURLFORM_COPYNAME, "ph",
                 CURLFORM_COPYCONTENTS, "7.0",
                 CURLFORM_END);

    curl_formadd(&formpost,
                 &lastptr,
                 CURLFORM_COPYNAME, "output",
                 CURLFORM_COPYCONTENTS, "text_long",
                 CURLFORM_END);

    curl_formadd(&formpost,
                 &lastptr,
                 CURLFORM_COPYNAME, "Submit",
                 CURLFORM_COPYCONTENTS, "Submit sequences",
                 CURLFORM_END);

    sprintf(url, URLWALTZ);
    //cout    << "Retrieving " << url << endl;
    
    curl = curl_easy_init();

    if (curl) {

      curl_easy_setopt(curl, CURLOPT_ERRORBUFFER,      errorBuffer);
      curl_easy_setopt(curl, CURLOPT_URL,              url);
      curl_easy_setopt(curl, CURLOPT_HEADER,           0);
      curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,   1);
      curl_easy_setopt(curl, CURLOPT_HTTPPOST,         formpost);
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,    writer);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA,       &bufferURL);

      result = curl_easy_perform(curl);
      curl_easy_cleanup(curl);

      string        resultQuery;
      
      if (result == CURLE_OK) {

        size_t  foundInit, foundEnd;
        string  str;
        
        str = "<meta http-equiv=\"REFRESH\" content=\"0;url="; 
        foundInit = bufferURL.find(str);
        str = "\">";                                           
        foundEnd  = bufferURL.find(str, foundInit  + 1);
          
        string resultHttp = bufferURL.substr(foundInit + strlen("<meta http-equiv=\"REFRESH\" content=\"0;url="), foundEnd - foundInit - strlen("<meta http-equiv=\"REFRESH\" content=\"0;url="));
        
        cout << resultHttp.c_str() << endl;
        
        //Read the result
        curl = curl_easy_init();
        
        if (curl) {
            
          curl_easy_setopt(curl, CURLOPT_ERRORBUFFER,      errorBuffer);
          curl_easy_setopt(curl, CURLOPT_URL,              resultHttp.c_str());
          curl_easy_setopt(curl, CURLOPT_HEADER,           0);
          curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,   1);
          curl_easy_setopt(curl, CURLOPT_HTTPPOST,         formpost);
          curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,    writer);
          curl_easy_setopt(curl, CURLOPT_WRITEDATA,       &resultQuery);
          
          result = curl_easy_perform(curl);
          curl_easy_cleanup(curl);

          if (result == CURLE_OK) {
          
            resultHttp.erase(resultHttp.length() - 5, 5);
            resultHttp.append(".zip");
            cout << resultHttp.c_str() << endl;
            
            char commandExec[LENGHTINLINE + 1];

            cout << "---------------------------------------------------------------------------------" << endl;
            sprintf(commandExec, "wget %s\0", resultHttp.c_str());
            cout << commandExec << endl;
            system(commandExec);
            
            cout << "---------------------------------------------------------------------------------" << endl;
            sprintf(commandExec, "unzip %s\0", SplitFilename(resultHttp).c_str());
            cout << commandExec << endl;
            system(commandExec);
              
            cout << "---------------------------------------------------------------------------------" << endl;
            sprintf(commandExec, "rm %s\0", SplitFilename(resultHttp).c_str());
            cout << commandExec << endl;
            system(commandExec);
              
            string resultTxt = SplitFilename(resultHttp).c_str();
                    
            resultTxt.erase(resultTxt.length() - 4, 4);
            resultTxt.append(".txt");
            
            cout << resultTxt.c_str() << endl;
            
            fstream fileResult;
            
            fileResult.open(resultTxt.c_str(), fstream::in);
            
            string  outResult        = "";            
            string  idFasta          = "";
            
            int     cumulativeLength = 0;
            int     totalRegions     = 0;
            
            if (fileResult.is_open()) {
                
              while (!fileResult.eof()) {
                  
                fileResult.getline(buffer, LENGHTINLINE);
                
                if (strstr(buffer, ">")) {

                  if (outResult != "") {
                    
                    fileOut << outResult.c_str() << "# " << totalRegions << "," << cumulativeLength << ",";
                    cout    << outResult.c_str() << "# " << totalRegions << "," << cumulativeLength << ",";
                    fasta *temporalSearch = listData->FirstOcurrence(SpaceFASTA::EqualID, &idFasta);
                    
                    if (temporalSearch) {
                      fileOut << (double)((double)cumulativeLength*100/(double)strlen(temporalSearch->sequence)) << endl;                        
                      cout    << (double)((double)cumulativeLength*100/(double)strlen(temporalSearch->sequence)) << endl;                        
                    }
                                        
                  }

                  outResult  = "";
                  outResult += buffer;
                  outResult += "#";

                  Ctrim(buffer);
                  
                  idFasta          = buffer;
                  cumulativeLength = 0;
                  totalRegions     = 0;

                }
                else if (strstr(buffer, "Positions") || strlen(buffer) < 2) {
                  continue;  
                }
                else {

                  char bufferCopy[LENGHTINLINE + 1];  
                    
                  strcpy(bufferCopy, buffer);
                  char* range   = SplitColumn(bufferCopy, 1, " \n\t\r");
                  
                  strcpy(bufferCopy, buffer);
                  char* segment = SplitColumn(bufferCopy, 2, " \n\t\r");
                  
                  strcpy(bufferCopy, buffer);
                  char* score   = SplitColumn(bufferCopy, 3, " \n\t\r");
                  
                  outResult += range;
                  outResult += ",";
                  outResult += segment;
                  outResult += ",";
                  outResult += score;
                  outResult += ";";
                  
                  totalRegions++;
                  cumulativeLength += strlen(segment);
                  
                }
                
              }
              
              if (outResult != "") {

                fileOut << outResult.c_str() << "# " << totalRegions << "," << cumulativeLength << ",";
                cout    << outResult.c_str() << "# " << totalRegions << "," << cumulativeLength << ",";
                fasta *temporalSearch = listData->FirstOcurrence(SpaceFASTA::EqualID, &idFasta);

                if (temporalSearch) {
                  fileOut << (double)((double)cumulativeLength*100/(double)strlen(temporalSearch->sequence)) << endl;                        
                  cout    << (double)((double)cumulativeLength*100/(double)strlen(temporalSearch->sequence)) << endl;                        
                }

              }
              
            }
            
            fileResult.close();
            
            resultHttp.erase(resultHttp.length() - 4, 4);
            
            cout << "---------------------------------------------------------------------------------" << endl;
            sprintf(commandExec, "rm %s*\0", resultTxt.c_str());
            cout << commandExec << endl;
            system(commandExec);
            cout << "---------------------------------------------------------------------------------" << endl;
              
          }
          
        }
      
      }

    }
    
    query             = "";
    cumulativeCounter = 0;
    
  }
  
  fileIn.close();
  fileOut.close();

  if (listData)    delete listData;
  
  if (namefileIn)  delete namefileIn;
  if (namefileOut) delete namefileOut;

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//


